from rest_framework import serializers
from rest_framework.serializers import Serializer

from bookmanagment_2.models import BooInfo, HeroInfo


def abort_django(value):
    if 'django' not in value:
        raise serializers.ValidationError("书名须含有django")


class BooInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    """
    指定需要进行序列化的字段,名称与模型类中一致
    作用,在进行序列化输出时,会将列出的属性(字段)进行输出
    在进行反序列化输入时,将接受对应的值
    serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义,serializer是独于数据库之外的存在
    read_only 表名该字段仅用于序列化输出,默认False(未开启),表名该字段将不能被用于反序列化使用
    write_only  表明该字段仅用于反序列化输入，默认False
    required  表明该字段在反序列化时必须输入，默认True(开启)
    label表示标签,是在html页面显示api时,显示的字段名称
    """
    # 验证1: 指定字段时通过字段的序列化类型和选项参数验证
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20, validators=[abort_django])
    bpub_date = serializers.DateField(label='发布日期')
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False, allow_null=True)

    # 验证2: 通过定义的方法验证,这些方法drf会自动调用
    # 对btitle字段进行筛选,对应的改成其他字段即可筛选其他字段
    def validate_btitle(self, value):
        """
        本函数的函数命名格式为:validate_字段名
        value表示请求报文中btitle值
        """
        if 'django' not in value:
            raise serializers.ValidationError("书名须含有django")
        # 验证后返回原值
        return value

    # 对多个字段进行验证
    def validate(self, attrs):
        bread = attrs.get('bread')
        bcomment = attrs.get('bcomment')
        if all([bread, bcomment]):
            if bread < bcomment:
                raise serializers.ValidationError("阅读量小于评论量")
        return attrs

    # 验证3: 在字段中添加validators选项参数(validators=[abort_django]), 须事先定义筛选函数(如 abort_django())

    # validate_data 验证通过后,数据保存在这此字典中

    # 增
    def create(self, validated_data):
        # 例:获取验证后的数据
        btitle = validated_data.get('btitle')
        print("btitle----", btitle)

        # 获取在构造序列化器时额外添加的属性值
        print(self.context.get("btest_context"))  # print(self.context)  # {'btest_context': 'abbbbb'}

        # **validated_data 对validated_data字典进行解包,k1=v1,k2=v2,...
        # create()方法Creates a new object with the given kwargs, saving it to the database and returning the created object.
        book = BooInfo.objects.create(**validated_data)
        return book

    # 改   btitle   bpub_date
    def update(self, instance, validated_data):
        # instance 当前待修改的模型类对象
        instance.btitle = validated_data.get('btitle')
        instance.bput_date = validated_data.get('bpub_date')
        if validated_data.get('bread'):
            instance.bread = validated_data.get('bread')
        if validated_data.get('bcomment'):
            instance.bcomment = validated_data.get('bcomment')
        # 将修改后的数据保存在数据库中
        instance.save()
        return instance


class HeroInfoSerializer(serializers.Serializer):
    """人物数据序列化器"""
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    id = serializers.IntegerField(label='ID', read_only=True)
    hname = serializers.CharField(label='名字', )
    hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label="性别", required=False)
    # allow_null 是否允许为Null/空
    hcomment = serializers.CharField(label="描述信息", max_length=200, required=False, allow_null=True)

    # hbook此字段将被序列化为关联对象的主键,即hook_id
    # hbook = serializers.PrimaryKeyRelatedField(label="图书", read_only=True)
    # hbook此字段将被序列化为关联对象的字符串表达方式(即__str__方法的返回值)
    # hbook = serializers.StringRelatedField(label="图书")
    # hbook使用关联对象的序列化器,返回的是关联的对象(即关联的书的信息对象)
    hbook = BooInfoSerializer()


"""
ModelSerializer类继承自Serializer类, 但提供了:
    基于模型类自动生成一系列字段
    基于模型类自动为Serializer生成validators,如unique_together
    包含默认的create()和update()的实现
"""
class HeroInfoModelSerializer(serializers.ModelSerializer):
    # 关系属性(hbook),默认输出主键,但是若设置(depth,那么就输出对应的对象)
    # 关系属性(hbook)这里默认显示所有(除隐藏字段外)字段(由Meta类中的fields = '__all__'指定)
    # hbook = BooInfoSerializer()  # hbook值显示是关联的对象(其中,依据BooInfoSerializer筛选的字段),也可以换成其他方式如: PrimaryKeyRelatedField/StringRelatedField

    # 隐藏的关系属性,需要定义才能输出
    # hbook_id = serializers.IntegerField()

    class Meta:
        model = HeroInfo  # 指明参照哪个模型类
        fields = '__all__'  # 指明为模型类的哪些字段生成,或者指明部分字段['...','...','...','...'],或者排除部分字段['...','...','...','...']
        # depth = 1  # 默认ModelSerializer使用主键作为关联字段,但是可以使用depth来简单的生成嵌套表示,depth为整数,表名嵌套的层级数量
        # 对字段设置额外约束
        extra_kwargs = {
            "hcomment": {
                "required": False,
                "max_length": 200
            },
            "hbook": {
                "required": False
            }
        }
    # 对hname筛选
    def validate_hname(self, value):
        if not value.startswith('马'):
            raise serializers.ValidationError('必须姓马')
        return value


